#ifndef SLLIST_H_
#define SLLIST_H_

//Forward declaration
template<typename Type> class SLLIter;

//class SLList
template<typename Type> class SLList
{
	//the iterator is the list's friend
	friend class SLLIter<Type>;

	struct Node
	{
		Type Info;
		Node *Next;
	};

	Node *Head;
	unsigned int Size;

public:
	void RecursiveFunction(Node *Rec);

	SLList();
	~SLList();
	SLList<Type>& operator=(const SLList<Type>& that);
	SLList(const SLList<Type>& that);
	void addHead(const Type& v);
	void clear();
	void insert(SLLIter<Type>& index, const Type& v);
	void remove(SLLIter<Type>& index);
	inline unsigned int size() const;

};

//class SLLIter
template<typename Type> class SLLIter
{
	//The list is the iterator's friend
	friend class SLList<Type>;
	typename SLList<Type>::Node *Current;
	typename SLList<Type>::Node *Previous;
	SLList<Type> *SList;

public:

	SLLIter(SLList<Type>& listToIterate);
	void begin();
	bool end() const;
	SLLIter<Type>& operator++();
	Type& current() const;


};

#pragma region SLList

template<typename Type>
void SLList<Type>::RecursiveFunction(Node *Rec)
{
	if(Rec == nullptr)
		return;
	
	if(Rec->Next != nullptr)
		RecursiveFunction(Rec->Next);

	addHead(Rec->Info);
}

/////////////////////////////////////////////////////////////////////////////
// Function : Constructor
// Notes : constructs an empty list
/////////////////////////////////////////////////////////////////////////////
template<typename Type>
SLList<Type>::SLList() : Head(nullptr), Size(0)
{

}

/////////////////////////////////////////////////////////////////////////////
// Function : Destructor
// Notes : Destroys the list
/////////////////////////////////////////////////////////////////////////////
template<typename Type>
SLList<Type>::~SLList()
{
	while(Head != nullptr)
	{
		Node *Temporary;
		Temporary = Head;
		Head = Head->Next;
		delete Temporary;
	}
}

/////////////////////////////////////////////////////////////////////////////
// Function : Assignment Operator
/////////////////////////////////////////////////////////////////////////////
template<typename Type>
SLList<Type>& SLList<Type>::operator=(const SLList<Type>& that)
{
	if(&that != this)
	{
		clear();
		RecursiveFunction(that.Head);
		Size = that.Size;
	}

	return *this;
}

/////////////////////////////////////////////////////////////////////////////
// Function : Copy Constructor
/////////////////////////////////////////////////////////////////////////////
template<typename Type>
SLList<Type>::SLList(const SLList<Type>& that)
{
	Head = nullptr;
	Size = 0;
	clear();
	RecursiveFunction(that.Head);
}

/////////////////////////////////////////////////////////////////////////////
// Function : addHead
// Parameters :	v - the item to add to the list
/////////////////////////////////////////////////////////////////////////////
template<typename Type>
void SLList<Type>::addHead(const Type& v)
{
	if(Size == 0)
	{
		Node *Temporary = new Node;
		Temporary->Next = nullptr;
		Temporary->Info = v;
		Head = Temporary;
		Size = 1;
	}
	else
	{
		Node *Temporary = new Node;
		Temporary->Next = Head;
		Temporary->Info = v;
		Head = Temporary;
		Size = Size + 1;
	}
}

///////////////////////////////////////
// Function : clear
// Notes : clears the list, freeing any dynamic memory
/////////////////////////////////////////////////////////////////////////////
template<typename Type>
void SLList<Type>::clear()
{
	Node *Temporary;
	while(Head != nullptr)
	{
		Temporary = Head;
		Head = Head->Next;
		delete Temporary;
	}
	Size = 0;
}

/////////////////////////////////////////////////////////////////////////////
// Function : insert
// Parameters :	index - an iterator to the location to insert at
//				v - the item to insert
// Notes : do nothing on a bad iterator
/////////////////////////////////////////////////////////////////////////////
template<typename Type>
void SLList<Type>::insert(SLLIter<Type>& index, const Type& v)
{
	if(index.Current == nullptr)	
		return;

	Node *Temporary = new Node;
	Temporary->Info = v;

	if(index.Previous == NULL)
	{		
		Temporary->Next = Head;
		index.Current = Temporary;
		Head = Temporary;
	}
	else
	{
		index.Previous->Next = Temporary;
		Temporary->Next = index.Current;
		index.Current = Temporary;
	}
	Size = Size + 1;
}

/////////////////////////////////////////////////////////////////////////////
// Function : remove
// Parameters :	index - an iterator to the location to remove from
// Notes : do nothing on a bad iterator
/////////////////////////////////////////////////////////////////////////////
template<typename Type>
void SLList<Type>::remove(SLLIter<Type>& index)
{
	if(index.Current == NULL)
		return;

	Node *Temporary = index.Current->Next;
	if(index.Current == Head)
	{
		Head = Head->Next;
	}
	else
	{
		index.Previous->Next = index.Current->Next;		
	}

	delete index.Current;
	index.Current = Temporary;
	Size = Size - 1;
}

/////////////////////////////////////////////////////////////////////////////
// Function : size
// Return : the number of items stored in the linked list.
/////////////////////////////////////////////////////////////////////////////
template<typename Type>
inline unsigned int SLList<Type>::size() const
{
	return Size;
}

#pragma endregion


#pragma region SLIter

/////////////////////////////////////////////////////////////////////////////
// Function : Constructor
// Parameters :	listToIterate - the list to iterate
/////////////////////////////////////////////////////////////////////////////
template<typename Type>
SLLIter<Type>::SLLIter(SLList<Type>& listToIterate)
{
	SList = &listToIterate;
}

/////////////////////////////////////////////////////////////////////////////
// Function : begin
// Notes : moves the iterator to the head of the list
/////////////////////////////////////////////////////////////////////////////
template<typename Type>
void SLLIter<Type>::begin()
{
	Current = SList->Head;
	Previous = nullptr;
}

/////////////////////////////////////////////////////////////////////////////
// Function : end
// Notes : returns true if we are at the end of the list, false otherwise
/////////////////////////////////////////////////////////////////////////////
template<typename Type>
bool SLLIter<Type>::end() const
{
	if(Current == nullptr)
		return true;
	else
		return false;
}

/////////////////////////////////////////////////////////////////////////////
// Function : operator++
// Notes : move the iterator forward one node
/////////////////////////////////////////////////////////////////////////////
template<typename Type>
SLLIter<Type>& SLLIter<Type>::operator++()
{
	if(Current != nullptr)
	{
		Previous = Current;
		Current = Current->Next;
	}
	return *this;
}

/////////////////////////////////////////////////////////////////////////////
// Function : current
// Notes : returns the item at the current iterator location
/////////////////////////////////////////////////////////////////////////////
template<typename Type>
Type& SLLIter<Type>::current() const
{
	return Current->Info;
}

#pragma endregion

#endif